#!/bin/bash
# Copyright 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# The "get_hash_for_release" command can be used in one of two forms:
#  - It can be used to return the hash associated with the project in
#    the current directory:
#
#      ~/chromiumos/chromite$ get_hash_for_release 6822.0.0
#      0b325183cf2eee7f93d7d631a8639c089f4a2d4f
#      # This is the hash of the "chromite" package for release 6822.0.0
#
#  - It can indicate what is the earliest release where a given hash
#    appeared:
#
#     ~/chromiumos/chromite$ get_hash_for_release R41 \
#         8eba257003357ca945528fb93dadd2c4d16b7d11
#     Oldest release with 8eba257003357ca945528fb93dadd2c4d16b7d11 is 6674.0.0


fetch_manifests() {
  temp_dir=/tmp/chrome_manifest_temp
  if [[ ! -d "${temp_dir}" ]] ; then
    mkdir -p "${temp_dir}"
    local git_server="https://chrome-internal.googlesource.com"
    git clone -q --depth 1 --single-branch \
        "${git_server}/chromeos/manifest-versions" "${temp_dir}"
  else
    (cd "${temp_dir}" && git pull -q)
  fi
}

cleanup_manifests() {
  if [[ -d "${temp_dir}" ]] ; then
    rm -rf "${temp_dir}"
  fi
}

is_child_release() {
  # Release numbers are stated in a set of dotted integers.
  # 2.0.0.0 is a child of 1.0.0.0 (later release)
  # 2.1.0.0 is a child of 2.0.0.0 (new branch)
  # 3.0.0.0 is a child of 2.0.0.0 but not 2.1.0.0 since 2.1 branched from 2.0.
  # 3.0.1.0 isn't really valid (branches are left aligned) but we ignore this.

  local child
  local parent
  IFS='.' read -a child <<< "$1"
  IFS='.' read -a parent <<< "$2"
  if [[ ${#child[@]} -ne ${#parent[@]} ]] ; then
    return 1
  fi

  local must_be_root
  for idx in $(seq 0 $((${#child[@]} - 1))); do
    local parent_num="${parent[idx]}"
    [[ -n "${must_be_root}" && "$parent_num" != "0" ]] && return 1
    local child_num="${child[idx]}"
    [[ "${parent_num}" > "${child_num}" ]] && return 1
    [[ "${parent_num}" < "${child_num}" ]] && must_be_root=1
  done
  return 0
}

get_project_info() {
  repo info . 2>/dev/null | egrep '^(Project|Current revision):' | cut -f2 -d:
}

get_revision() {
  local release="$1"
  local revision="$2"
  grep "${project}.*upstream=.*${branch}" \
      ${temp_dir}/buildspecs/$release/${revision}.xml |
      sed -e 's/ *<project.*revision="\([^"]*\)".*/\1/' \
          -e 's/^.*\/\([0-9.]*\)\.xml:/\1 : /'
}

get_hash_for_release() {
  get_revision '*' "${revision}"
}

get_release_for_hash() {
  local release="${revision:1}"
  local -a all_revs=($(ls "${temp_dir}/buildspecs/${release}" |
                       sort -Vr | sed -e 's/\.xml$//'))
  latest_rev=$(basename $(cd "${temp_dir}/buildspecs/${release}" &&
                          git log -n1 --name-only --oneline --grep release . |
                          grep -v ' ' | sort -V | tail -1 |
                          sed -e 's/\.xml$//'))
  revs=()
  for rev in "${all_revs[@]}"; do
    is_child_release "${latest_rev}" "${rev}" && revs+=("${rev}")
  done
  last_released_hash=$(get_revision "${release}" "${latest_rev}" |
                       awk '{print $1}')
  if [[ -z "${last_released_hash}" ]] ; then
    echo "Huh?  Can't find hash for ${latest_rev}"
    return
  fi
  local -a hashes
  found=''
  for search_size in 10 100 1000 10000; do
    hashes=($(git log --pretty=format:%H -n "${search_size}" \
              "${last_released_hash}"))
    if echo "${hashes[*]}" | grep -q $hash; then
      found=1
      break
    fi
  done
  if [[ -z "${found}" ]] ; then
    echo "Perhaps ${hash} was never in release ${release}?"
    return
  fi
  # Pop all hashes after the one we care about.
  while [[ "${hashes[-1]}" != "${hash}" ]] ; do
    unset hashes[${#hashes[@]}-1]
  done
  oldest_release="${latest_rev}"
  idx=1
  while [[ $idx -lt ${#revs[@]} ]]; do
    rev="${revs[idx]}"
    hash_for_rev=$(get_revision "${release}" "${rev}" |
                   awk '{print $1}')
    if ! echo "${hashes[*]}" | grep -q $hash_for_rev; then
      break
    fi
    oldest_release="${rev}"
    idx=$(( idx + 1 ))
  done
  echo "Oldest release with ${hash} is ${oldest_release}"
}

main() {
  revision="${1}"
  if [[ -z "${revision}"  ]] ; then
    echo "Usage: $0 <revision> [hash]"
    exit 0
  fi

  if expr "${revision}" : '[MR]' > /dev/null; then
    hash="${2}"
    shift
  fi

  project_info=($(get_project_info))

  if [[ "${#project_info[@]}" < 2 ]] ; then
    echo "Couldn't get project info (are you in a repo directory?)"
    exit 0
  fi

  project="${project_info[0]}"
  branch="$(echo ${project_info[1]} | cut -d/ -f3)"
  if [[ "${branch}" == "master" ]] ; then
    branch=""
  elif [[ "${branch}" == "chromeos-3.4" ]] ; then
    # Hack for old releases of chromeos-kernel.
    branch='\.[4B]"'
  fi

  fetch_manifests

  if [[ -z "${hash}" ]] ; then
    get_hash_for_release
  else
    get_release_for_hash
  fi

  #cleanup_manifests
}

main "$@"
